from abc import ABC, abstractmethod

import sqlalchemy
import os
import redis
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.ext.declarative import declarative_base
from typing import AnyStr, Optional, Dict, Tuple, ByteString


class Client(ABC):

    @abstractmethod
    def select(self, *args, **kwargs):
        pass

    @abstractmethod
    def insert(self, *args, **kwargs):
        pass

    @abstractmethod
    def update(self, *args, **kwargs):
        pass

    @abstractmethod
    def delete(self, *args, **kwargs):
        pass


class OracleClient(Client):

    def __init__(self,
                 host: AnyStr,
                 port: int,
                 database: AnyStr,
                 username: AnyStr,
                 password: AnyStr,
                 mode: Optional[AnyStr] = None,
                 **kwargs):
        os.environ["NLS_LANG"] = "GERMAN_GERMANY.UTF8"  # 解决中文乱码
        self._host = host
        self._port = port
        self._database = database
        self._mode = mode
        self._username = username
        self._password = password
        self.db_engine = None
        self.meta = None
        self._session: Optional[Session] = None

        self.__make_engine()
        self.__make_session()

    @property
    def session(self):
        return self._session

    @session.setter
    def session(self, _: Session):
        self._session = _

    def __make_engine(self):

        if self._host and self._port and self._database and self._username and self._password:
            if not self._mode:
                self.db_engine = sqlalchemy.create_engine('oracle+cx_oracle://%s:%s@%s:%s/%s' % (
                    self._username, self._password, self._host, self._port,
                    self._database
                ), echo=True, )
            else:
                self.db_engine = sqlalchemy.create_engine('oracle+cx_oracle://%s:%s@%s:%s/%s?mode=%s' % (
                    self._username, self._password, self._host, self._port,
                    self._database, self._mode
                ), echo=True, )

    def __make_session(self):
        session_maker = sessionmaker(bind=self.db_engine)
        self.session = session_maker()

    def select(self, condition: AnyStr,
               table: AnyStr,
               where: Optional[AnyStr] = "1=1",
               **kwargs):
        if kwargs:
            kwargs = self.remake_kw(kwargs)
        SQLScript = "select %s from %s where  %s %s;" % (condition, table, where, kwargs)
        return self.__execute(SQLScript)

    def __execute(self, script: AnyStr):
        result = self._session.execute(script)
        return result.fetchall()

    @staticmethod
    def remake_kw(self, params: Dict):
        sql = ''
        for k, v in params.items():
            sql += "%s=%s" % (k, v)
        return sql

    def insert(self, table: AnyStr,
               row: Tuple[AnyStr],
               value: Tuple[AnyStr]):
        SQLScript = 'insert into %s (%s) values (%s)' % (table, *row, *value)
        return self.__execute(SQLScript)

    def update(self, table: AnyStr,
               row: AnyStr,
               new_value: AnyStr,
               condition: AnyStr):
        SQLScript = "update %s set %s=%s where %s" % (table, row, new_value, condition)
        return self.__execute(SQLScript)

    def delete(self, table: AnyStr,
               condition: AnyStr):
        SQLScript = "delete from %s where %s" % (table, condition)
        return self.__execute(SQLScript)


class MysqlClient(Client):

    def __init__(self,
                 host: AnyStr,
                 username: AnyStr,
                 password: AnyStr,
                 port: Optional[int] = 3306,
                 database: Optional[AnyStr] = None,
                 **kwargs,
                 ):
        self._username = username
        self._password = password
        self._host = host
        self._port = port
        self._database = database
        self._session = None

        if not self._username or self._password or self._host:
            return

        self._engine = None
        self.__set_engine()
        self.__set_session()

    def __set_engine(self):

        if self._database:
            self._engine = sqlalchemy.create_engine(
                "mysql+pymysql://%s:%s@%s:%s/%s" % (
                    self._username, self._password, self._host, self._port, self._database),
                encoding="utf-8",
                echo=True
            )
        else:
            self._engine = sqlalchemy.create_engine(
                "mysql+pymysql://%s:%s@%s:%s/" % (self._username, self._password, self._host, self._port),
                encoding="utf-8",
                echo=True
            )

    def __set_session(self):
        Base = declarative_base()
        Base.metadata.create_all(self._engine)
        session_maker = sessionmaker(self._engine)
        self.session = session_maker()

    def select(self, *args, **kwargs):
        pass

    def insert(self, *args, **kwargs):
        pass

    def update(self, *args, **kwargs):
        pass

    def delete(self, *args, **kwargs):
        pass


class RedisClient:

    def __init__(self,
                 host: AnyStr,
                 port: Optional[int] = 6379,
                 username: Optional[AnyStr] = 'root',
                 password: Optional[AnyStr] = '',
                 db_index: Optional[int] = 0,
                 Pool: bool = False):
        self._host = host
        self._port = port
        self._username = username
        self._password = password
        self._db_index = db_index
        self._pool = Pool
        self._session: Optional[redis.Redis] = None
        self.__set_session()

    def __set_session(self):
        if not self._pool:
            self._session = redis.Redis(host=self._host, username=self._username, port=self._port,
                                        password=self._password, db=self._db_index)
        else:
            connection_pool = redis.ConnectionPool(host=self._host, username=self._username, port=self._port,
                                                   password=self._password, db=self._db_index)
            self._session = redis.Redis(connection_pool)

    def set(self, key, value, *args, **kwargs):
        return self._session.set(key, value, *args, **kwargs)

    def get(self, key) -> ByteString:
        return self._session.get(key)

    """mget, mset等不添加了,只提供这几个DEMO"""

from lib.files import ReadFileData